package cn.xinfei.xdecision.data.core.frame.executors.http;

import cn.xinfei.xdecision.common.model.datax.consts.VariableConst;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.VarDataSourceCallLog;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.Variable;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.VariableCallLog;
import cn.xinfei.xdecision.common.model.datax.enums.*;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import cn.xinfei.xdecision.common.service.datax.datainterface.v2.VarInterfaceInfoService;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.VariableService;
import cn.xinfei.xdecision.data.core.cache.VarInterfaceCache;
import cn.xinfei.xdecision.data.core.cache.VarMetaDataCache;
import cn.xinfei.xdecision.data.core.frame.executors.HttpExecutorFactory;
import cn.xinfei.xdecision.data.core.frame.executors.IExecutorService;
import cn.xinfei.xdecision.data.core.frame.executors.collect.VariableHelper;
import cn.xinfei.xdecision.data.core.frame.listener.VarDataSourceLogEvent;
import cn.xinfei.xdecision.data.core.frame.strategy.StrategyContext;
import cn.xinfei.xdecision.data.core.frame.strategy.VariableStrategy;
import cn.xinfei.xdecision.data.core.util.VarDataSourceCallLogUtil;
import cn.xinfei.xdecision.data.core.util.VariableCallLogUtils;
import cn.xinfei.xdecision.data.core.vo.StandardHttpExecutorResultEntity;
import cn.xinfei.xdecision.data.core.vo.VarDatasourceDTO;
import cn.xinfei.xdecision.data.core.vo.VarInterfaceInfoVO;
import cn.xinfei.xdecision.data.core.vo.standard.HttpStandardVarRequest;
import cn.xinfei.xdecision.data.core.vo.standard.HttpStandardVarResponse;
import cn.xinfei.xdecision.data.service.VarDataSourceCallLogService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HttpStandardExecutorService implements IExecutorService<StandardHttpExecutorResultEntity> {

    @Autowired
    HttpExecutorFactory httpExecutorFactory;

    @Autowired
    VarInterfaceInfoService varInterfaceInfoService;

    @Autowired
    VariableService variableService;

    @Autowired
    VarDataSourceCallLogService varDataSourceCallLogService;

    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    VarInterfaceCache varInterfaceCache;

    @Autowired
    VarMetaDataCache varMetaDataCache;

    @Autowired
    StrategyContext strategyContext;

    @Override
    public StandardHttpExecutorResultEntity run(Set<String> varCodes,
                                                Map<String, Object> inputParam,
                                                Long datasourceId,
                                                CollectStageEnum collectStageEnum,
                                                String requestId) {
        try {
            return runStandardHttpExecutor(varCodes, inputParam, datasourceId, collectStageEnum, requestId);
        } catch (Exception e) {
            log.error("requestId = {} StandardHttpExecutor error", requestId, e);
            StandardHttpExecutorResultEntity resultEntity = new StandardHttpExecutorResultEntity();
            resultEntity.setStandardVarResult(VariableHelper.fillVarWithDefaultValueWhenRespEmpty(variableService.findVariables(varCodes)));
            resultEntity.setCode(String.valueOf(XDecisionDataExceptionType.DATA_HTTP_STANDARD_EXECUTOR_ERROR.code));
            resultEntity.setMessage(XDecisionDataExceptionType.DATA_HTTP_STANDARD_EXECUTOR_ERROR.text);
            return resultEntity;
        }finally {
            MDC.remove(VariableConst.HANDLE_NAME);
            MDC.remove(VariableConst.COLLECT_STAGE);
            MDC.remove(VariableConst.REQUEST_ID);
            MDC.clear();
        }
    }

    private StandardHttpExecutorResultEntity runStandardHttpExecutor(Set<String> varCodes,
                                                                     Map<String, Object> inputParam,
                                                                     Long datasourceId,
                                                                     CollectStageEnum collectStageEnum,
                                                                     String requestId) {
        log.info("requestId = {}, HttpStandardExecutor start run varCodes: {},datasourceId: {}",
                requestId,
                JSONObject.toJSONString(varCodes),
                datasourceId);
        Transaction catT = Cat.newTransaction(VarCatTypeEnum.STANDARD_EXECUTOR_EXECUTE.getCode(), VarCatTypeEnum.STANDARD_EXECUTOR_EXECUTE.getName());
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        StandardHttpExecutorResultEntity executorResultEntity = new StandardHttpExecutorResultEntity();
        executorResultEntity.setExecType(ExecutorEnum.HTTP.name());
        Map<String, Object> executorResult = Maps.newConcurrentMap();
        List<Variable> variables;
//        List<VariableCallLog> varCallLogList = new ArrayList<>();
        Optional<Map<String, Variable>> loadVarMetaDataCache = Optional.ofNullable(varMetaDataCache.loadVarMetaData());
        HttpStandardVarResponse standardVarResponse;
        try {
            VarInterfaceInfoVO varInterfaceInfoVO = (VarInterfaceInfoVO) httpExecutorFactory.buildExecutorInput(datasourceId);
            ExecutorType executorType = ExecutorType.fromIsSyncCode(varInterfaceInfoVO.getIsSync());
            log.info("requestId = {} 开始请求外部数据源,InterfaceName: {},url: {},",
                    requestId,
                    varInterfaceInfoVO.getName(),
                    varInterfaceInfoVO.getUrl());

            VariableStrategy strategy = strategyContext.getStrategy(VariableStrategyType.HTTP_STAND.name());
            HttpStandardVarRequest httpStandardVarRequest = buildHttpStandardReqest(requestId,
                    varInterfaceInfoVO,
                    executorType,
                    new JSONObject(inputParam));
            standardVarResponse = (HttpStandardVarResponse) strategy.process(httpStandardVarRequest);

            JSONObject standardHttpResponse = JSON.parseObject(JSON.toJSONString(standardVarResponse.getData()));
            log.info("requestId = {} 【HttpStandardExecutor】 response result: {}",
                    requestId,
                    JSONObject.toJSONString(standardHttpResponse));
            log.info("requestId = {} 开始解析接口变量...", requestId);
            if (loadVarMetaDataCache.isPresent()) {
                log.info("requestId = {} 【HttpStandardExecutor】查询缓存 VarMetaDataCache",
                        requestId);
                variables = Optional.of(
                        loadVarMetaDataCache.get().values()
                                .stream().filter(var -> varCodes.contains(var.getVarCode()))
                                .collect(Collectors.toList())).orElseGet(() -> {
                    log.info("requestId = {} VarMetaDataCache缓存 未查得接口源配置", requestId);
                    return variableService.findVariables(varCodes);
                });
            } else {
                variables = variableService.findVariables(varCodes);
            }
            for (Variable var : variables) {
                Object varValue = getVarValueByInterface(var, inputParam, standardHttpResponse);
                if (Objects.isNull(varValue)) {
                    log.info("requestId = {} varCode = {} 变量解析结果为空,默认值为 = {}",
                            requestId,
                            var.getVarCode(),
                            var.getVarDefaultValue());
                    varValue = VariableHelper.getDefaultNullValue(var);
                }

//                VariableCallLog variableCallLog = VariableCallLogUtils.buildVarCallLog(
//                        var.getVarCode(),
//                        inputParam,
//                        JSONObject.toJSONString(varValue),
//                        datasourceId,
//                        VariableEnum.INTERFACE_VAR,
//                        collectStageEnum,
//                        SourceType.THIRD_PARTY,
//                        stopWatch.getStartTime(),
//                        requestId);
//                varCallLogList.add(variableCallLog);
                executorResult.put(var.getVarCode(), varValue);
            }
            VarDataSourceCallLog varDataSourceCallLog = VarDataSourceCallLogUtil.buildVarDataSourceCallLog(
                    stopWatch.getStartTime(),
                    System.currentTimeMillis(),
                    JSON.toJSONString(inputParam),
                    JSON.toJSONString(standardHttpResponse),
                    requestId,
                    JSONObject.toJSONString(executorResult),
                    SourceType.THIRD_PARTY.name(),
                    collectStageEnum.getCode(),
                    datasourceId
            );
            //异步保存datasource log
            VarDatasourceDTO varDatasourceDTO = new VarDatasourceDTO();
            varDatasourceDTO.setRequestId(requestId);
            varDatasourceDTO.setVarDataSourceCallLog(varDataSourceCallLog);
            varDatasourceDTO.setCollectStage(collectStageEnum.name());
            applicationContext.publishEvent(new VarDataSourceLogEvent(
                    this, varDatasourceDTO
            ));
            executorResultEntity.setStandardVarResult(executorResult);
//            executorResultEntity.setVarCallLogList(varCallLogList);
            catT.setStatus(Transaction.SUCCESS);
            return executorResultEntity;
        } catch (Exception e) {
            log.error("异常：msg={}", e.getMessage(), e);
            Cat.logError(e);
            catT.setStatus(e);
            throw new XDecisionDataException(XDecisionDataExceptionType.DATA_HTTP_STANDARD_EXECUTOR_ERROR, "Http-Standard 执行器执行异常", e);
        } finally {
            stopWatch.stop();
            log.info("requestId = {} 任务执行 StandardHttpDataSource 总耗时:" + stopWatch.getTime() + " MillSeconds", requestId);
            catT.setDurationInMillis(stopWatch.getTime());
            catT.complete();

        }

    }

    private HttpStandardVarRequest buildHttpStandardReqest(String requestId,
                                                           VarInterfaceInfoVO varInterfaceInfoVO,
                                                           ExecutorType executorType,
                                                           JSONObject inputParams) {
        HttpStandardVarRequest httpStandardVarRequest = new HttpStandardVarRequest();
        httpStandardVarRequest.setExecorType(executorType);
        httpStandardVarRequest.setVarInterfaceInfoVO(varInterfaceInfoVO);
        httpStandardVarRequest.setRequestId(requestId);
        httpStandardVarRequest.setInputParams(inputParams);
        return httpStandardVarRequest;
    }


    /**
     * 解析接口变量
     *
     * @param var
     * @return
     */
    public Object getVarValueByInterface(Variable var,
                                         Map<String, Object> inputParam,
                                         JSONObject httpResponse) {
        Map response = JSONObject.parseObject(JSONObject.toJSONString(httpResponse), Map.class);

        //解析接口变量
        Object resultValue;
        try {
            resultValue = HttpExecutorUtil.interfaceParseVariable(var.getInterfaceParseVar(), JSONObject.toJSONString(response));
            log.info("【解析接口响应体完成】varCode: {},varName: {}, varValue: {}, response: {}", var.getVarCode(), var.getVarName(), resultValue, response);
        } catch (Exception e) {
            log.error("【解析接口响应体失败】varCode:{},varName:{},response:{}", var.getVarCode(), var.getVarName(), response, e);
            e.printStackTrace();
            throw new XDecisionDataException(XDecisionDataExceptionType.DATA_GET_INTERFACE_VAR_ERROR, "解析接口变量异常");
        }
        return resultValue;
    }


}
